import { Color, FileLoader, Object3D, Scene } from "three";
import { resCache } from "./ResCache";

export interface JsonAtomicPart {
    file?: string;
    name?: string;
    quaternion: number[];
    position: number[];
    scale: number[];
}

export interface JsonActor {
    name: string,
    actorType: string,
    position: [number, number, number],
    quaternion: [number, number, number],
    scale: [number, number, number],
    deviceId?: string,
    sn?: string,
    children: JsonActor[],
    userData: any,
    parts: JsonAtomicPart[];
    visible: boolean;
}

interface Prefab {
    /*场景的模型*/
    meshs:JsonActor[]
}

export function LoadActors(url: string) {
    return new Promise<{ [key: string]: object }>((resolve, reject) => {
        const loader = new FileLoader();
        loader.load(
            url,
            (data)=>{
                const scene = JSON.parse(data as string) as Prefab;
                let result = __parseFromJson(scene);
                resolve(result);
            },
            (xhr)=> {
                console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
            },
            (err)=>{
                console.error(err);
                reject(err);
            }
        );
        
        
    })
}

export function LoadModel(scene: Scene) {
    return new Promise<void>((resolve, reject) => {
        let files: Part[] = [];
        scene.traverseVisible((child)=>{
            if((child as Part).isPart) {
                let part = (child as Part);
                if(part.file && part.file.endsWith('.glb')) {
                    files.push(part);
                }
            }
        })
    
        let workCount = files.length;
        let succedCount = files.length;
        for (let i = 0; i < files.length; i++) {
            const f = files[i];
            resCache.getModel(f.file as any, (root)=>{
                workCount--;
                if(root) {
                    f.add(root);
                    console.log("加载 " + f.file + " 成功~")
                    succedCount--;
                    if(workCount == 0){
                        if(succedCount == 0) {
                            resolve();
                        }
                        else {
                            reject(workCount);
                        }
                    }
                }
                else {
                    console.log("加载 " + f.file + " 失败！")
                }
            })
            
        } 
    })
}

function __parseActor(parnt: Object3D, json: JsonActor, out: any){
    if(!json.visible && json.actorType !== 'point') return;

    let obj;
    if(json.actorType === 'guandao') {
        obj = new GuandaoActor();
    }
    else if(json.actorType === 'static') {
        for (let i = 0; i < json.parts.length; i++) {
            const part = json.parts[i];
            if (part.file?.startsWith('CCW') || part.file?.startsWith('SCW')) {
                obj = new MachineActor();
                break;
            }
        }
    }
    
    if(!obj) obj = new Actor();
    obj.fromJson(json);
    parnt.add(obj);

    if(out[json.actorType] == undefined) {
        out[json.actorType] = [];
    }
    out[json.actorType].push(obj);
    
    if(json.deviceId && json.deviceId !== "") {
        out['__device'].push(obj);
    }

    if(json.children) {
        for (let n = 0; n < json.children.length; n++) {
            const child = json.children[n];
            __parseActor(obj, child, out);
        }
    }
}

function __parseFromJson(prfab: Prefab){
    let scene = new Scene();
    let out = {
        __root: scene,
        __device: []
    }
    for (let i = 0; i < prfab.meshs?.length; i++) {
        const data = prfab.meshs[i];
        __parseActor(scene, data, out);
    }

    return out;
}

export class Part extends Object3D {
    file?: string;
    isPart: boolean = true;
    fromJson(json: JsonAtomicPart) {
        this.file = json.file;
        this.name = json.name || "";
        this.position.fromArray(json.position);
        this.quaternion.fromArray(json.quaternion);
        this.scale.fromArray(json.scale);
    }
}

export class Actor extends Object3D {
    actorType: string = ""
    deviceId: string | undefined;
    sn: string | undefined = undefined
    isMachine: boolean = false;
    isPump: boolean = false;

    fromJson(json: JsonActor) {
        this.name = json.name;
        this.position.fromArray(json.position);
        this.quaternion.fromArray(json.quaternion);
        this.scale.fromArray(json.scale);

        this.actorType = json.actorType;
        this.sn = json.sn;
        this.deviceId = json.deviceId;

        this.userData = json.userData;
        for (let i = 0; i < json.parts.length; i++) {
            const part = json.parts[i];
            if (part.file?.startsWith('CCW') || part.file?.startsWith('SCW')) {
                this.isMachine = true;
            }

            if(part.file && part.file.indexOf('水泵') >= 0) {
                this.isPump = true;
            }
        }
        
    }
}

export class MachineActor extends Actor {
    
    fromJson(json: JsonActor) {
        super.fromJson(json);
        for (let i = 0; i < json.parts.length; i++) {
            const partjson = json.parts[i];
            const part = new Part();
            part.fromJson(partjson);
            this.add(part);
        }
    }
}

export class GuandaoActor extends Actor {
    private __color: Color = new Color(0xffffff);

    fromJson(json: JsonActor): void {
        super.fromJson(json);
        if(json.userData.color) this.color.set(json.userData.color);
    }

    get points() {
        let points = []
        for (let i = 0; i < this.children.length; i++) {
            const c = this.children[i] as Actor;
            if(c.actorType === 'point') {
                points.push(c);
            }
        }

        return points;
    }

    get radius() {
        return this.userData.radius;
    }

    get flowDir() {
        return this.userData.flowDir;
    }

    set flowDir(dir: boolean) {
        this.userData.flowDir = dir;
    }

    get color() {
        return this.__color;
    }
}